<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>11.力导向图2</title>
    <script src="./node_modules/d3/dist/d3.min.js"></script>
</head>
<body>
<svg width="960" height="600"></svg>
<script>
    let marge = {top: 60, bottom: 60, left: 60, right: 60}
    let svg = d3.select("svg")
    let width = svg.attr("width")
    let height = svg.attr("height")
    let g = svg.append("g")
        .attr("transform", "translate(" + marge.top + "," + marge.left + ")");

    //准备数据
    let nodes = [
        {name: "湖南邵阳"},
        {name: "山东莱州"},
        {name: "广东阳江"},
        {name: "山东枣庄"},
        {name: "泽"},
        {name: "恒"},
        {name: "鑫"},
        {name: "明山"},
        {name: "班长"}
    ];

    let edges = [
        {source: 0, target: 4, relation: "籍贯", value: 1.3},
        {source: 4, target: 5, relation: "舍友", value: 1},
        {source: 4, target: 6, relation: "舍友", value: 1},
        {source: 4, target: 7, relation: "舍友", value: 1},
        {source: 1, target: 6, relation: "籍贯", value: 2},
        {source: 2, target: 5, relation: "籍贯", value: 0.9},
        {source: 3, target: 7, relation: "籍贯", value: 1},
        {source: 5, target: 6, relation: "同学", value: 1.6},
        {source: 6, target: 7, relation: "朋友", value: 0.7},
        {source: 6, target: 8, relation: "职责", value: 2}
    ];
    // 设置一个color的颜色比例尺，为了让不同的扇形呈现不同的颜色
    let colorScale = d3.scaleOrdinal()
        .domain(d3.range(nodes.length))
        .range(d3.schemeCategory10);

    // 新建一个力导向图
    let forceSimulation = d3.forceSimulation()
        .force("link", d3.forceLink())
        .force("charge", d3.forceManyBody())  // 创建一个电荷作用力模型
        .force("center", d3.forceCenter());  // 创建一个中心作用力

    // 初始化力导向图，也就是传入数据
    // 生成节点数据
    forceSimulation.nodes(nodes)
    // 这个函数对于力导向图来说非常重要，因为力导向图是不断运动的，每一时刻都在发生更新，所以需要不断更新节点和连线的位置
        .on("tick", ticked);//这个函数很重要，后面给出具体实现和说明
    //生成边数据
    forceSimulation.force("link")
        .links(edges)
        .distance(function (d) {// 每一边的长度
            return d.value * 100;
        })
    //设置图形的中心位置
    forceSimulation.force("center")
        .x(width / 2)
        .y(height / 2);
    //在浏览器的控制台输出
    console.log(nodes);
    console.log(edges);

    //有了节点和边的数据后，我们开始绘制
    //绘制边
    let links = g.append("g")
        .selectAll("line")
        .data(edges)
        .enter()
        .append("line")
        .attr("stroke", function (d, i) {
            return colorScale(i);
        })
        .attr("stroke-width", 1);
    let linksText = g.append("g")
        .selectAll("text")
        .data(edges)
        .enter()
        .append("text")
        .text(function (d) {
            return d.relation;
        })

    //绘制节点
    //老规矩，先为节点和节点上的文字分组
    let gs = g.selectAll(".circleText")
        .data(nodes)
        .enter()
        .append("g")
        .attr("transform", function (d, i) {
            let cirX = d.x;
            let cirY = d.y;
            return "translate(" + cirX + "," + cirY + ")";
        })
        .call(d3.drag()
            .on("start", started)
            .on("drag", dragged)
            .on("end", ended)
        );

    //绘制节点
    gs.append("circle")
        .attr("r", 10)
        .attr("fill", function (d, i) {
            return colorScale(i);
        })
    //文字
    gs.append("text")
        .attr("x", -10)
        .attr("y", -20)
        .attr("dy", 10)
        .text(function (d) {
            return d.name;
        })

    function ticked() {
        links
            .attr("x1", function (d) {
                return d.source.x;
            })
            .attr("y1", function (d) {
                return d.source.y;
            })
            .attr("x2", function (d) {
                return d.target.x;
            })
            .attr("y2", function (d) {
                return d.target.y;
            });

        linksText
            .attr("x", function (d) {
                return (d.source.x + d.target.x) / 2;
            })
            .attr("y", function (d) {
                return (d.source.y + d.target.y) / 2;
            });

        gs
            .attr("transform", function (d) {
                return "translate(" + d.x + "," + d.y + ")";
            });
    }

    function started(d) {
        if (!d3.event.active) {
            forceSimulation.alphaTarget(0.8).restart();
        }
        d.fx = d.x;
        d.fy = d.y;
    }

    function dragged(d) {
        d.fx = d3.event.x;
        d.fy = d3.event.y;
    }

    function ended(d) {
        if (!d3.event.active) {
            forceSimulation.alphaTarget(0);
        }
        d.fx = null;
        d.fy = null;
    }
</script>
</body>
</html>
